package model.quadtree.graph;

import java.awt.Rectangle;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import control.Config;

import model.quadtree.Node;
import model.quadtree.Type;


@SuppressWarnings("rawtypes")
public class QuadTree2Graph {
	private Node2Graph root;
	//private boolean debug = true;
	private boolean stopVerifying;
	protected static final int minSize = 40;
	private Hashtable leafNodes;
	private double[][] edges;

	public QuadTree2Graph(Node2Graph root) {
		this.root = root;
		this.stopVerifying = false;
		leafNodes = new Hashtable();
	}

	public void verifyGreys(List<Rectangle> obstacles){
		while(!stopVerifying){
			stopVerifying = true;
			classifyNodes(obstacles);
		}
	}
	
	public void classifyNodes(List<Rectangle> obstacles)  
	{  
		preOrder(this.root, obstacles);
	}

	@SuppressWarnings("unchecked")
	public void makeChildren(Node node){
		node.makeChildren();
		stopVerifying = false;
		leafNodes.remove(node.getId());
		leafNodes.put(node.getChild()[0].getId(), node.getChild()[0]);
		leafNodes.put(node.getChild()[1].getId(), node.getChild()[1]);
		leafNodes.put(node.getChild()[2].getId(), node.getChild()[2]);
		leafNodes.put(node.getChild()[3].getId(), node.getChild()[3]);
		if(debug()){	
			System.out.println("Removendo:"+node.getId());
			System.out.println("Adicionando:"+
					node.getChild()[0].getId()+","+
					node.getChild()[1].getId()+","+
					node.getChild()[2].getId()+","+
					node.getChild()[3].getId());
			System.out.println();
		}
	}
	
	public void verifyNeighborhood(){
		edges = new double[Node2Graph.idGenerator][Node2Graph.idGenerator];
		for(int i=0;i<edges.length;i++){
			for(int j=0;j<edges.length;j++){
				edges[i][j]=Double.MAX_VALUE;
			}
		}
		Enumeration i = leafNodes.elements();
		Node2Graph current;
		Node2Graph sweep;
		double distance;
		while(i.hasMoreElements()){
			Enumeration j = leafNodes.elements();
			current = (Node2Graph)i.nextElement();
			while(j.hasMoreElements()){
				sweep = (Node2Graph)j.nextElement();
				if(i!=j && (sideNeighbor(current, sweep) || frontBackNeighbor(current, sweep))
						&& (current.getType()==Type.WHITE && sweep.getType()==Type.WHITE)){
					distance = getDistance(current, sweep);
					edges[current.getId()][sweep.getId()] = distance;
				}
			}
		}
	}
	
	private boolean sideNeighbor(Node2Graph current, Node2Graph sweep){
		if((sweep.getVert1().x==current.getVert2().x || sweep.getVert2().x==current.getVert1().x) &&
				(sweep.getVert1().y>=current.getVert1().y && sweep.getVert2().y<=current.getVert2().y ||
				 current.getVert1().y>=sweep.getVert1().y && current.getVert2().y<=sweep.getVert2().y))
			return true;
		return false;
	}
	
	private boolean frontBackNeighbor(Node2Graph current, Node2Graph sweep){
		if((sweep.getVert1().y==current.getVert2().y || sweep.getVert2().y==current.getVert1().y) &&
				(sweep.getVert1().x<=current.getVert1().x && sweep.getVert2().x>=current.getVert2().x ||
				 current.getVert1().x<=sweep.getVert1().x && current.getVert2().x>=sweep.getVert2().x))
			return true;
		return false;
	}
	
	public double[][] getEdges() {
		return edges;
	}

	public void setEdges(double[][] edges) {
		this.edges = edges;
	}

	private double getDistance(Node2Graph a, Node2Graph b){
		int xa = Math.min(a.getCenter().x, b.getCenter().x);
		int xb = Math.max(a.getCenter().x, b.getCenter().x);
		int ya = Math.min(a.getCenter().y, b.getCenter().y);
		int yb = Math.max(a.getCenter().y, b.getCenter().y);
		
		return Math.sqrt(Math.pow(xb-xa, 2)+Math.pow(yb-ya,2));
	}

	protected void debug(Node child){
		if(Config.debugQuadTree2Graph){
			System.out.println("Node " + child.getId() + ":");
			if(child.getParent()!=null)
				System.out.println("   " + "Parent: "+child.getParent().getId());
			else
				System.out.println("   " + "is the ROOT");
			System.out.println("   " + "Type: "+child.getType());
			System.out.println("   " + "Vert1: "+child.getVert1());
			System.out.println("   " + "Vert2: "+child.getVert2());
			System.out.println();
		}
	}

	public Node getRoot() {
		return root;
	}

	public void setRoot(Node2Graph root) {
		this.root = root;
	}

	public boolean debug(){
		return Config.debugQuadTree2Graph;
	}

	private void preOrder(Node2Graph node, List<Rectangle> obstacles)  
	{
		if(node == null)  
			return;
		node.classifyNode2Graph(obstacles); 
		debug(node);
		if(node.getChild()!=null){
			preOrder(node.getChild()[0], obstacles);
			preOrder(node.getChild()[1], obstacles);
			preOrder(node.getChild()[2], obstacles);
			preOrder(node.getChild()[3], obstacles);
		} 
		else{
			if(node.getType()==Type.GREY){
				if(node.getVert1().x - node.getVert2().x > minSize){
					makeChildren(node);
				}
			}
		}
	}

	public Hashtable getLeafNodes() {
		return leafNodes;
	}

	public void setLeafNodes(Hashtable leafNodes) {
		this.leafNodes = leafNodes;
	}
}
